<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta property="og:description" content="samuel-24276的博客">
    <meta property="og:type" content="website">
    <meta name="description" content="samuel-24276的博客">
    <meta name="keyword"  content="aircloud">
    <link rel="shortcut icon" href="/myblog/img/favicon.ico">

    <title>
        
        std::enable_shared_from_this使用 - samuel-24276的博客 | samuel-24276&#39;s Blog
        
    </title>

    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/myblog/css/aircloud.css">

    
<link rel="stylesheet" href="/myblog/css/gitment.css">

    <!--<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">-->
    <link href="//at.alicdn.com/t/font_620856_28hi1hpxx24.css" rel="stylesheet" type="text/css">
    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 7.1.1"></head>

<body>

<div class="site-nav-toggle" id="site-nav-toggle">
    <button>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
    </button>
</div>

<div class="index-about">
    <i> Like what you do, do what you like </i>
</div>

<div class="index-container">
    
    <div class="index-left">
        
<div class="nav" id="nav">
    <div class="avatar-name">
        <div class="avatar ">
            <img src="/myblog/img/avatar.jpg" />
        </div>
        <div class="name">
            <i>samuel-24276</i>
        </div>
    </div>
    <div class="contents" id="nav-content">
        <ul>
            <li >
                <a href="/myblog/">
                    <i class="iconfont icon-shouye1"></i>
                    <span>主页</span>
                </a>
            </li>
            <li >
                <a href="/myblog/tags">
                    <i class="iconfont icon-biaoqian1"></i>
                    <span>标签</span>
                </a>
            </li>
            <li >
                <a href="/myblog/archives">
                    <i class="iconfont icon-guidang2"></i>
                    <span>存档</span>
                </a>
            </li>
            <li >
                <a href="/myblog/collect/">
                    <i class="iconfont icon-shoucang1"></i>
                    <span>收藏</span>
                </a>
            </li>
            <li >
                <a href="/myblog/about/">
                    <i class="iconfont icon-guanyu2"></i>
                    <span>关于</span>
                </a>
            </li>
            
            <li>
                <a id="search">
                    <i class="iconfont icon-sousuo1"></i>
                    <span>搜索</span>
                </a>
            </li>
            
        </ul>
    </div>
    
        <div id="toc" class="toc-article">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF1"><span class="toc-text">使用场景1</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E5%92%8C%E5%AE%9E%E6%88%98"><span class="toc-text">原理和实战</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF2"><span class="toc-text">使用场景2</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8F%82%E8%80%83%E6%96%87%E7%AB%A0"><span class="toc-text">参考文章</span></a></li></ol>
</div>
    
</div>


<div class="search-field" id="search-field">
    <div class="search-bg" id="search-bg"></div>
    <div class="search-container">
        <div class="search-input">
            <span id="esc-search"> <i class="icon-fanhui iconfont"></i></span>
            <input id="search-input"/>
            <span id="begin-search">搜索</span>
        </div>
        <div class="search-result-container" id="search-result-container">

        </div>
    </div>
</div>

        <div class="index-about-mobile">
            <i> Like what you do, do what you like </i>
        </div>
    </div>
    
    <div class="index-middle">
        <!-- Main Content -->
        


<div class="post-container">
    <div class="post-title">
        std::enable_shared_from_this使用
    </div>

    <div class="post-meta">
        <span class="attr">发布于：<span>2022-11-12 09:00:21</span></span>
        
        <span class="attr">标签：/
        
        <a class="tag" href="/myblog/tags/#C++" title="C++">C++</a>
        <span>/</span>
        
        
        </span>
        <span class="attr">访问：<span id="busuanzi_value_page_pv"></span>
</span>
</span>
    </div>
    <div class="post-content no-indent">
        <h1 id="使用场景1"><a href="#使用场景1" class="headerlink" title="使用场景1"></a>使用场景1</h1><p>在很多场合，经常会遇到一种情况，<strong>如何安全的获取对象的this指针</strong>，一般来说我们不建议直接返回this指针，可以想象下有这么一种情况，<strong>返回的this指针保存在外部一个局部&#x2F;全局变量，当对象已经被析构了，但是外部变量并不知道指针指向的对象已经被析构了，如果此时外部使用了这个指针就会发生程序奔溃</strong>。既要像指针操作对象一样，又能安全的析构对象，很自然就想到，智能指针就很合适！</p>
<p>那么智能指针如何使用呢？现在我们来看一段代码。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Widget</span>&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Widget</span>()&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Widget constructor run&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">Widget</span>()&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Widget destructor run&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">std::shared_ptr&lt;Widget&gt; <span class="title">GetSharedObject</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> std::<span class="built_in">shared_ptr</span>&lt;Widget&gt;(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::shared_ptr&lt;Widget&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> Widget())</span></span>;</span><br><span class="line">    std::shared_ptr&lt;Widget&gt; q = p-&gt;<span class="built_in">GetSharedObject</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; p.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; q.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编译运行后程序输出如下：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Widget constructor run</span><br><span class="line">1</span><br><span class="line">1</span><br><span class="line">Widget destructor run</span><br><span class="line">Widget destructor run</span><br><span class="line">22:06:45: 程序异常结束。</span><br></pre></td></tr></table></figure>

<p>从输出我们可以看到，调用了一次构造函数，却调用了两次析构函数，很明显这是不正确的。而std::enable_shared_from_this正是为了解决这个问题而存在。</p>
<h1 id="原理和实战"><a href="#原理和实战" class="headerlink" title="原理和实战"></a>原理和实战</h1><p>前面我们说使用std::enable_shared_from_this能解决安全获取this指针的问题。在使用之前，我们先来了解下std::enable_shared_from_this是什么？为什么能解决这个问题？std::enable_shared_from_this定义如下：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">class</span> <span class="title class_">_Tp</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">_LIBCPP_TEMPLATE_VIS</span> enable_shared_from_this</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">mutable</span> weak_ptr&lt;_Tp&gt; __weak_this_;</span><br><span class="line"><span class="keyword">protected</span>:</span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR</span></span><br><span class="line"><span class="function">    <span class="title">enable_shared_from_this</span><span class="params">()</span> _NOEXCEPT </span>&#123;&#125;</span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY</span></span><br><span class="line"><span class="function">    <span class="title">enable_shared_from_this</span><span class="params">(enable_shared_from_this <span class="type">const</span>&amp;)</span> _NOEXCEPT </span>&#123;&#125;</span><br><span class="line">    _LIBCPP_INLINE_VISIBILITY</span><br><span class="line">    enable_shared_from_this&amp; <span class="keyword">operator</span>=(enable_shared_from_this <span class="type">const</span>&amp;) _NOEXCEPT</span><br><span class="line">        &#123;<span class="keyword">return</span> *<span class="keyword">this</span>;&#125;</span><br><span class="line">    _LIBCPP_INLINE_VISIBILITY</span><br><span class="line">    ~<span class="built_in">enable_shared_from_this</span>() &#123;&#125;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY</span></span><br><span class="line"><span class="function">    shared_ptr&lt;_Tp&gt; <span class="title">shared_from_this</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;<span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;_Tp&gt;(__weak_this_);&#125;</span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY</span></span><br><span class="line"><span class="function">    shared_ptr&lt;_Tp <span class="type">const</span>&gt; <span class="title">shared_from_this</span><span class="params">()</span> <span class="type">const</span></span></span><br><span class="line"><span class="function">        </span>&#123;<span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;<span class="type">const</span> _Tp&gt;(__weak_this_);&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">if</span> _LIBCPP_STD_VER &gt; 14</span></span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY</span></span><br><span class="line"><span class="function">    weak_ptr&lt;_Tp&gt; <span class="title">weak_from_this</span><span class="params">()</span> _NOEXCEPT</span></span><br><span class="line"><span class="function">       </span>&#123; <span class="keyword">return</span> __weak_this_; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">_LIBCPP_INLINE_VISIBILITY</span></span><br><span class="line"><span class="function">    weak_ptr&lt;<span class="type">const</span> _Tp&gt; <span class="title">weak_from_this</span><span class="params">()</span> <span class="type">const</span> _NOEXCEPT</span></span><br><span class="line"><span class="function">        </span>&#123; <span class="keyword">return</span> __weak_this_; &#125;</span><br><span class="line"><span class="meta">#<span class="keyword">endif</span> <span class="comment">// _LIBCPP_STD_VER &gt; 14</span></span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Up</span>&gt; <span class="keyword">friend</span> <span class="keyword">class</span> <span class="title class_">shared_ptr</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>std::enable_shared_from_this是模板类，内部有个_Tp类型weak_ptr指针，调用shared_from_this成员函数便可获取到_Tp类型智能指针，从这里可以看出，_Tp类型就是我们的目标类型。再来看看<strong>std::enable_shared_from_this的构造函数都是protected，因此不能直接创建std::enable_from_shared_from_this类的实例变量，只能作为基类使用</strong>。因此使用方法如下代码所示：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Widget</span> : <span class="keyword">public</span> std::enable_shared_from_this&lt;Widget&gt;&#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">Widget</span>()&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Widget constructor run&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line">    ~<span class="built_in">Widget</span>()&#123;</span><br><span class="line">        std::cout &lt;&lt; <span class="string">&quot;Widget destructor run&quot;</span> &lt;&lt; std::endl;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">std::shared_ptr&lt;Widget&gt; <span class="title">GetSharedObject</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">shared_from_this</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="function">std::shared_ptr&lt;Widget&gt; <span class="title">p</span><span class="params">(<span class="keyword">new</span> Widget())</span></span>;</span><br><span class="line">    std::shared_ptr&lt;Widget&gt; q = p-&gt;<span class="built_in">GetSharedObject</span>();</span><br><span class="line"></span><br><span class="line">    std::cout &lt;&lt; p.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line">    std::cout &lt;&lt; q.<span class="built_in">use_count</span>() &lt;&lt; std::endl;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里为<strong>什么要创建智能指针p而不是直接创建裸指针p？根本原因在于std::enable_shared_from_this内部的weak_ptr</strong>，若只是创建裸指针p，那么p被delete后仍然面对不安全使用内部this指针问题。因此p只能被定义为智能指针。<em>当p被定义为shared_ptr智能指针后，p指针引用计数是1(weak_ptr不会增加引用计数)，再通过shared_from_this获取内部this指针的智能指针，则p的引用计数变为2</em>。</p>
<p>现编译运行输出如下：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Widget constructor run</span><br><span class="line">2</span><br><span class="line">2</span><br><span class="line">Widget destructor run</span><br></pre></td></tr></table></figure>

<p>正确的返回了智能指针，p和q的引用计数都是2，且只调用了一次构造函数和析构函数，不会错误的析构对象多次。</p>
<h1 id="使用场景2"><a href="#使用场景2" class="headerlink" title="使用场景2"></a>使用场景2</h1><p>在解释<code>std::enable_shared_from_this</code>之前，先看一个<code>std::shared_ptr</code>典型用法：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    std::shared_ptr&lt;<span class="type">int</span>&gt; pt1&#123; <span class="keyword">new</span> <span class="type">int</span>&#123; <span class="number">10</span> &#125; &#125;;</span><br><span class="line">    <span class="keyword">auto</span> pt2&#123; pt1 &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这时<code>pt1</code>和<code>pt2</code>共用了引用计数，当<code>pt1</code>和<code>pt2</code>的生命周期都结束时，<code>new int&#123;10&#125;</code>分配的内存会被释放。下面的做法会导致内存多次释放，因为它们没有使用共同的引用计数：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> pt&#123; <span class="keyword">new</span> <span class="type">int</span>&#123; <span class="number">10</span> &#125; &#125;;</span><br><span class="line">    std::shared_ptr&lt;<span class="type">int</span>&gt; pt1&#123; pt &#125;;</span><br><span class="line">    std::shared_ptr&lt;<span class="type">int</span>&gt; pt2&#123; pt &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>当然，我想应该也没有人这么使用<code>std::shared_ptr</code>(通常一个资源被一个shared_ptr管理)。不过下面这个错误倒是比较常见：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SomeAPI</span><span class="params">(<span class="type">const</span> std::shared_ptr&lt;SomeData&gt;&amp; d)</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span> &#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">NeedCallSomeAPI</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 需要用this调用SomeAPI</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上面这段代码需要在<code>NeedCallSomeAPI</code>函数中调用<code>SomeAPI</code>，而<code>SomeAPI</code>需要的是一个<code>std::shared_ptr&lt;SomeData&gt;</code>的实参。这个时候应该怎么做？</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span> &#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">NeedCallSomeAPI</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="built_in">SomeAPI</span>(std::shared_ptr&lt;SomeData&gt;&#123;<span class="keyword">this</span>&#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上面的做法是错误的，<strong>因为<code>SomeAPI</code>调用结束后<code>std::shared_ptr&lt;SomeData&gt;</code>对象的引用计数会降为0，导致<code>this</code>被意外释放。</strong></p>
<p>这种情况下，我们需要使用<code>std::enable_shared_from_this </code>，使用方法很简单，只需要让<code>SomeData</code>继承<code>std::enable_shared_from_this&lt;SomeData&gt;</code>，然后调用<code>shared_from_this</code>吗，例如：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SomeAPI</span><span class="params">(<span class="type">const</span> std::shared_ptr&lt;SomeData&gt;&amp; d)</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span>:std::enable_shared_from_this&lt;SomeData&gt; &#123;</span><br><span class="line">    <span class="function"><span class="type">static</span> std::shared_ptr&lt;SomeData&gt; <span class="title">Create</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> std::<span class="built_in">shared_ptr</span>&lt;SomeData&gt;(<span class="keyword">new</span> SomeData);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">NeedCallSomeAPI</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="built_in">SomeAPI</span>(<span class="built_in">shared_from_this</span>());</span><br><span class="line">    &#125;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="built_in">SomeData</span>() &#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> d&#123; SomeData::<span class="built_in">Create</span>() &#125;;</span><br><span class="line">    d-&gt;<span class="built_in">NeedCallSomeAPI</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>std::enable_shared_from_this </code>的实现比较复杂，但是实现原理则比较简单。它内部使用了<code>std::weak_ptr</code>来帮助完成指针相关控制数据的同步，而这份数据是在创建<code>std::shared_ptr</code>的时候完成的。我们来重点解析这一点。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span>&lt;<span class="keyword">class</span> <span class="title class_">T</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">enable_shared_from_this</span> &#123;</span><br><span class="line"> <span class="keyword">mutable</span> weak_ptr&lt;T&gt; weak_this;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line"> <span class="function">shared_ptr&lt;T&gt; <span class="title">shared_from_this</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;T&gt;(weak_this); </span><br><span class="line"> &#125;</span><br><span class="line"> <span class="function">shared_ptr&lt;<span class="type">const</span> T&gt; <span class="title">shared_from_this</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">shared_ptr</span>&lt;<span class="type">const</span> T&gt;(weak_this); </span><br><span class="line"> &#125;</span><br><span class="line">...</span><br><span class="line"> <span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">U</span>&gt; <span class="keyword">friend</span> <span class="keyword">class</span> <span class="title class_">shared_ptr</span>;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>以上是摘要的<code>enable_shared_from_this</code>的代码，这份代码中有两个关键要素。首先<code>weak_this</code>被声明为<code>mutable</code>，这让<code>weak_this</code>可以在<code>const</code>的限定下修改，其次也是<strong>最关键的地方，该类声明了<code>shared_ptr</code>为友元。这意味着<code>std::shared_ptr</code>可以修改<code>weak_this</code>，并且<code>weak_this</code>被初始化的地方在<code>std::shared_ptr</code>中</strong>。进一步说，没有<code>std::shared_ptr</code>的<code>enable_shared_from_this</code>是没有灵魂的：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;memory&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span>;</span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">SomeAPI</span><span class="params">(<span class="type">const</span> std::shared_ptr&lt;SomeData&gt;&amp; d)</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">SomeData</span>:std::enable_shared_from_this&lt;SomeData&gt; &#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">NeedCallSomeAPI</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="built_in">SomeAPI</span>(<span class="built_in">shared_from_this</span>());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> d&#123; <span class="keyword">new</span> SomeData &#125;;</span><br><span class="line">    d-&gt;<span class="built_in">NeedCallSomeAPI</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在这份代码中调用<code>shared_from_this</code>会出错。</p>
<p>再深入一步，<code>std::shared_ptr</code>是如何判断实例化对象类型是否继承<code>std::enable_shared_from_this</code>，并且通过判断结果决定是否初始化<code>weak_this</code>的呢？答案是SFINAE(“<em>Substitution Failure Is Not An Error</em>“)。</p>
<p>让我们查看VS2019的STL代码：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Ty</span>&gt;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">enable_shared_from_this</span> &#123; </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">using</span> _Esft_type = enable_shared_from_this;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Yty</span>, <span class="keyword">class</span> = <span class="type">void</span>&gt;</span><br><span class="line"><span class="keyword">struct</span> _Can_enable_shared : false_type &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Yty</span>&gt;</span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">_Can_enable_shared</span>&lt;_Yty, <span class="type">void_t</span>&lt;<span class="keyword">typename</span> _Yty::_Esft_type&gt;&gt;</span><br><span class="line">    : is_convertible&lt;<span class="type">remove_cv_t</span>&lt;_Yty&gt;*, <span class="keyword">typename</span> _Yty::_Esft_type*&gt;::type &#123;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>这里的重点是<code>_Can_enable_shared</code>，如果目标类型有内嵌类型<code>_Esft_type</code>，并且目标类型和内嵌类型的指针是可转换的，也就是有继承关系，那么类型结果为<code>true_type</code>，反之为<code>false_type</code>。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">template</span> &lt;<span class="keyword">class</span> <span class="title class_">_Ux</span>&gt;</span><br><span class="line">   <span class="type">void</span> _Set_ptr_rep_and_enable_shared(_Ux* <span class="type">const</span> _Px, _Ref_count_base* <span class="type">const</span> _Rx) <span class="keyword">noexcept</span> &#123;</span><br><span class="line">       <span class="keyword">this</span>-&gt;_Ptr = _Px;</span><br><span class="line">       <span class="keyword">this</span>-&gt;_Rep = _Rx;</span><br><span class="line">       <span class="function"><span class="keyword">if</span> <span class="title">constexpr</span> <span class="params">(conjunction_v&lt;negation&lt;is_array&lt;_Ty&gt;&gt;, negation&lt;is_volatile&lt;_Ux&gt;&gt;, _Can_enable_shared&lt;_Ux&gt;&gt;)</span> </span>&#123;</span><br><span class="line">           <span class="keyword">if</span> (_Px &amp;&amp; _Px-&gt;_Wptr.<span class="built_in">expired</span>()) &#123;</span><br><span class="line">               _Px-&gt;_Wptr = shared_ptr&lt;<span class="type">remove_cv_t</span>&lt;_Ux&gt;&gt;(*<span class="keyword">this</span>, <span class="keyword">const_cast</span>&lt;<span class="type">remove_cv_t</span>&lt;_Ux&gt;*&gt;(_Px));</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

<p>接下来，如果对象不是数组、不是<code>volatile</code>声明的并且<code>_Can_enable_shared</code>返回<code>true_type</code>，那么<code>_Wptr</code>才会被初始化。<code>std::shared_ptr</code>的构造函数以及<code>std::make_shared</code>函数都会调用该函数。</p>
<h1 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h1><p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/393571228" title="std::enable_shared_from_this">std::enable_shared_from_this使用</a></p>
<p><a target="_blank" rel="noopener" href="https://0cch.com/2020/08/05/something-about-enable_shared_from_this/">std::enable_shared_from_this原理浅析 | 0CCh Blog</a></p>
<hr>

        
            <div class="donate-container">
    <div class="donate-button">
        <button id="donate-button">赞赏</button>
    </div>
    <div class="donate-img-container hide" id="donate-img-container">
        <img id="donate-img" src="" data-src="/myblog/img/donate.jpg">
        <p> 感谢鼓励 </p>
    </div>
</div>
        
        <br />
        <div id="comment-container">
        </div>
        <div id="disqus_thread"></div>
        <div id="lv-container"></div>
        <div class="giscus"></div>
    </div>
</div>

    </div>
</div>


<footer class="footer">
    <ul class="list-inline text-center">
        
        

        

        

        
        <li>
            <a target="_blank"  href="https://github.com/samuel-24276">
                            <span class="fa-stack fa-lg">
                                <i class="iconfont icon-github"></i>
                            </span>
            </a>
        </li>
        

        

    </ul>
    
    <p>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://github.com/samuel-24276">samuel-24276&#39;s Page</a></span>
        <span>/</span>
        
        <span><a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/">北京-未备案</a></span>
        <span>/</span>
        
        <span><a href="#">返回顶部</a></span>
        <span>/</span>
        
    </p>
    
    <p>
        <span id="busuanzi_container_site_pv">
            <span id="busuanzi_value_site_pv"></span>PV
        </span>
        <span id="busuanzi_container_site_uv">
            <span id="busuanzi_value_site_uv"></span>UV
        </span>
        Created By <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>  Theme <a target="_blank" rel="noopener" href="https://github.com/aircloud/hexo-theme-aircloud">AirCloud</a></p>
</footer>




</body>

<script>
    // We expose some of the variables needed by the front end
    window.hexo_search_path = "search.json"
    window.hexo_root = "/myblog/"
    window.isPost = true
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<script src="/myblog/js/index.js"></script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>






    <script type="text/javascript">
       (function() {
           if (typeof LivereTower === 'function') { return; }

           var j, d = document.getElementById('lv-container');

           d.setAttribute('data-id','city');
           d.setAttribute('data-uid' , 'MTAyMC80OTA5OC8yNTU5Mw==');

           j = document.createElement('script');
           j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
           j.async = true;

           d.appendChild(j);
       })();
    </script>
    <noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
    </div>

</html>
